package Groep1;

import java.util.*;

class ProductionRule {
    private Set<Terminal> starters;
    private Set<NonTerminal> startersStarters;
    private LinkedList<Element> rule;
    private NonTerminal parent;

    public ProductionRule(NonTerminal parent, LinkedList<Element> rule) {
        this.parent=parent;
        starters = new LinkedHashSet<Terminal>();
        startersStarters = new LinkedHashSet<NonTerminal>();
        this.rule=rule;
    }

    public LinkedList<Element> getElements() {
        return rule;
    }

    public void calculateStarters(Set<ProductionRule> exclude) {
        if(!rule.isEmpty()) {
            NonTerminal nt;
            Iterator<Element> i=rule.iterator();
            boolean empty=true;
            exclude.add(this);
            do {
                Element e=i.next();
                if(e instanceof Terminal) {
                    starters.add((Terminal)e);
                    empty=false;
                }
                else if(e instanceof NonTerminal) {
                    Set<Terminal> ntstarters = new LinkedHashSet<Terminal>();
                    nt = (NonTerminal)e;
                    LinkedList<ProductionRule> rules = nt.getProductionRules();
                    Iterator<ProductionRule> i2 = rules.iterator();
                    while(i2.hasNext()) {
                        ProductionRule r = i2.next();
                        if(!exclude.contains(r)) {
                            r.calculateStarters(exclude);
                            exclude.remove(r);
                            ntstarters.addAll(r.getStarters());
                        }
                    }
                    empty=empty&&ntstarters.contains(new Terminal("E"));
//				ntstarters.remove(new Terminal("E"));
                    startersStarters.add(nt);
                }
            } while(empty&&i.hasNext());
            if(empty)
                starters.add(new Terminal("E"));
        }
    }

    public int hashCode() {
        return parent.getName().hashCode() ^ rule.hashCode();
    }

    public Set<Terminal> getStarters() {
        return getStarters(new LinkedHashSet<ProductionRule>());
    }

    private Set<Terminal> getStarters(Set<ProductionRule> exclude) {
        Iterator<NonTerminal> i = startersStarters.iterator();
        Set<Terminal> retval = new LinkedHashSet<Terminal>();
        retval.addAll(starters);
        exclude.add(this);
        while(i.hasNext()) {
            Iterator<ProductionRule> i2 = i.next().getProductionRules().iterator();
            while(i2.hasNext()) {
                ProductionRule productionrule=i2.next();
                if(!exclude.contains(productionrule)) {
                    retval.addAll(productionrule.getStarters(exclude));
                }
            }
        }
        exclude.remove(this);
        return retval;
    }

    public String toString() {
        return "Production rule "+parent.getName()+": "+rule;
    }

}
